#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
 #include <string.h>

#include <sys/stat.h>
#include <fcntl.h>

#define USART1 "/dev/ttySAC1"


 /*
UART 接口一帧的数据格式为 1 个起始位， 8 个数据位、无奇偶校验位、 1 个停止位，波
特率固定为 9600。
*/
int init_tty(int fd)
{
    struct termios old_cfg,new_cfg;

    //1.得到当前串口的属性配置 int tcgetattr (int __fd, struct termios *__termios_p)
    if(tcgetattr(fd,&old_cfg) != 0)
    {

        perror("tcgetattr");
        return -1;
    }

    //2.设置串口的通信模式 void cfmakeraw (struct termios *__termios_p)
    //将新的配置属性清零
    bzero(&new_cfg,sizeof(new_cfg));

    new_cfg = old_cfg;
    cfmakeraw(&new_cfg);
    //3 配置串口的波特率 数据位 停止位  奇偶校验位
    //int cfsetispeed (struct termios *__termios_p, speed_t __speed)
    cfsetispeed(&new_cfg,B9600);
    cfsetospeed(&new_cfg,B9600);

    //激活选项：CLOCAL 和 CREAD 分别用于本地连接和接受使能
    new_cfg.c_cflag |=  CLOCAL | CREAD;

    //设置数据位:8位，需使用掩码设置
    new_cfg.c_cflag &= ~CSIZE;
    new_cfg.c_cflag |= CS8;
    //设置无校验
    new_cfg.c_cflag &= ~PARENB;
    //设置停止位：1个bit
     new_cfg.c_cflag &= ~CSTOPB;

     //4 清空缓冲区
      new_cfg.c_cc[VTIME]  = 0;
    new_cfg.c_cc[VMIN] = 4;
    tcflush(fd,TCIFLUSH);

    //5 将配置好的串口给设置到系统
    if(tcsetattr(fd,TCSANOW,&new_cfg) != 0)
    {
        perror("tcsetattr");
        return -1;
    }

    return 0;

}

//定义校验和
unsigned char calc_BCC(unsigned char*SerBfr,int len)
{

    unsigned char BCC = 0;
    int i = 0;
    for(; i<(SerBfr[0]-2); i++) {
         BCC ^= *(SerBfr + i);
    }
    return ~BCC;
}
//请求命令--A
int piccRequest(int fd)
{
    //[1] 定义数据格式
    unsigned char wbuf[128],rbuf[128];
    fd_set rfds;
   struct timeval tv;
    int retval;

    memset(wbuf,0,128);
    memset(rbuf,0,128);

    //[2] 主机向从机发起请求
    wbuf[0] = 0x07; //数据帧长度:0x07
    wbuf[1] = 0x02;//命令类型：ISO14443A类型
    wbuf[2] = 0x41;//具体的命令'A' --- 65
    wbuf[3] = 0x01;//信息的长度:1B
    wbuf[4] = 0x52;//具体的命令
    wbuf[5] = calc_BCC(wbuf,wbuf[0]-2);//校验和
    wbuf[6] = 0x03;//结束符
    //[3] 把数据写入到串口中
    write(fd,wbuf,7);

    //[4] 等待下位机的模块的响就 --- io复用来监听select
     /* Watch 串口 (fd ) to see when it has input. */
   FD_ZERO(&rfds);
   FD_SET(fd, &rfds);
    /* Wait up to 300 seconds. */
   tv.tv_sec = 300;
   tv.tv_usec = 0;

   //开启监听
   retval = select(fd + 1, &rfds, NULL, NULL, &tv);
   switch(retval)
   {

       case -1:
       perror("piccRequest");
       break;
       case 0:
       printf("timeout\n");
       break;
       default:
        //监听成功，可以读取数据
        retval = read(fd,rbuf,8);
        if(retval < 0)
        {
            printf("piccRequest = %d\n",retval);
        }

        if(rbuf[2] == 0x00)
        {
            printf("piccRequest sucess\n");
            return 0;
        }

   }

   FD_CLR(fd, &rfds);

    return 1;
}

//防碰撞
int piccAnticoll(int fd)
{
    //[1] 定义数据格式
    unsigned char wbuf[128],rbuf[128];
    fd_set rfds;
   struct timeval tv;
    int retval;
   unsigned int ID;
    memset(wbuf,0,128);
    memset(rbuf,0,128);

    //[2] 主机向从机发起请求
    wbuf[0] = 0x08; //数据帧长度:0x08
    wbuf[1] = 0x02;//命令类型：ISO14443A类型
    wbuf[2] = 0x42;//具体的命令'B' --- 66
    wbuf[3] = 0x02;//信息的长度:1B
    wbuf[4] = 0x93;//具体的数据
    wbuf[5] = 0x00;
    wbuf[6] = calc_BCC(wbuf,wbuf[0]-2);//校验和
    wbuf[7] = 0x03;//结束符
    //[3] 把数据写入到串口中
    write(fd,wbuf,8);
     /* Watch 串口 (fd ) to see when it has input. */
   FD_ZERO(&rfds);
   FD_SET(fd, &rfds);
    /* Wait up to 300 seconds. */
   tv.tv_sec = 300;
   tv.tv_usec = 0;

   //开启监听
   retval = select(fd + 1, &rfds, NULL, NULL, &tv);

   switch(retval)
   {

       case -1:
       perror("piccAnticoll");
       break;
       case 0:
       printf("timeout\n");
       break;
       default:
        //监听成功，可以读取数据
        retval = read(fd,rbuf,10);
        if(retval < 0)
        {
            printf("piccAnticoll = %d\n",retval);
        }

        if(rbuf[2] == 0x00)
        {
            printf("piccAnticoll sucess\n");
            //ID是存放在rbuf中4 5 6 7个元素中的内容
            ID = rbuf[7]<<24 | rbuf[6]<<16 | rbuf[5]<<8 | rbuf[4];

            printf("piccAnticoll the card id = %x\n",ID);
            return 0;
        }

   }

   FD_CLR(fd, &rfds);
    return 1;
}

//读取电子标签的ID
void readCardId(void)
{
    //[1] 打开串口

    int fd = open(USART1,O_RDWR);
    if(fd == -1)
    {
        perror("串口打开失败");
        return ;
    }

    //[2] 初始化串口
    init_tty(fd);

    //[3]根据开发文档中的访问设备的流程 ---请求(A) --->防碰撞(B)
    if(piccRequest(fd))
    {
        printf("piccRequest falied\n");
        return;
    }

    if(piccAnticoll(fd))
    {
        printf("piccAnticoll failed\n");
        return;
    }
}

int main(void)
{

    while(1)
    {
        readCardId();
        //每隔1S，读取数据
        sleep(1);
    }




    return 0;
}
